﻿#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="ContainerWidget.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2011 
//       Roland Rosenkranz (Glatzemann@email.de)
// </copyright>
// <license>
//   This file is part of starLiGHT.GUI.
//
//   It is released under the Microsoft Public License (Ms-PL).
//
//   You should have received a copy of the Microsoft Public License
//   along with starLiGHT.GUI. If not, see http://sle.codeplex.com/license.
//    
//   ADDITIONAL (commercial) LICENSES for starLiGHT.GUI are available on request.
// </license>
// <version>
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev::                       $:  Revision of last commit *
// * $Author::                    $:  Author of last commit   *
// * $Date::                      $:  Date of last commit     *
// ************************************************************
// </version>
//--------------------------------------------------------------------------------
#endregion

namespace starLiGHT.GUI.Widgets
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using starLiGHT.Collections;
    #endregion

    public class ContainerWidget : ScrollableWidget, IUpdateableWidget
    {
        #region Private Members
        private Vector2 lastMousePosition;
        private FastList<Widget> prevHits;
        private FastList<Widget> actHits;
        private Widget focusedWidget;
        private Widget focusBackup;
        #endregion

        #region Constructors
        public ContainerWidget(GuiManager manager, string name)
            : base(manager, name)
        {
            this.Init();
        }

        public ContainerWidget(GuiManager manager, string name, string layout)
            : base(manager, name, layout)
        {
            this.Init();
        }
        #endregion

        public override RectangleF ScissorRectangle
        {
            get
            {
                RectangleF scissor = ClientRectangle;
                //foreach (Widget w in this.Widgets)
                //{
                //    scissor = RectangleF.Union(scissor, w.ScissorRectangle);
                //}

                return scissor;
            }
        }

        public override bool CanFocus
        {
            get
            {
                return true; // actually this may be false... but then it can not contain any focusable updatable widget...
            }
        }

        public Widget ActiveWidget
        {
            get
            {
                return this.focusedWidget;
            }

            internal set
            {
                this.focusedWidget = value;
                if (value != null)
                {
                    this.focusBackup = value;
                }
            }
        }

        public override void OnGotFocus(object sender, EventArgs e)
        {
            base.OnGotFocus(sender, e);
            this.focusedWidget = this.focusBackup;
            if (this.focusedWidget != null)
            {
                this.focusedWidget.SetFocus();
            }
        }

        public override void OnLostFocus(object sender, EventArgs e)
        {
            base.OnLostFocus(sender, e);
            if (this.focusedWidget != null)
            {
                this.focusBackup = this.focusedWidget;
                this.focusBackup.Focused = false;
            }
        }

        public override void Draw(GameTime gameTime, IGuiRenderer renderer)
        {
            if (Visible && CurrentState != null)
            {
                foreach (Layer layer in CurrentState.Layers)
                {
                    renderer.DrawLayer(this, layer);
                }

                renderer.ScissorPush(this.ScissorRectangle);

                for (int i = 0; i < this.Widgets.Count; i++)
                {
                    Widget w = this.Widgets[i];

                    if (w.ScissorRectangle.Intersects(w.DisplayRectangle))
                    {
                        w.Draw(gameTime, renderer);
                    }
                }

                renderer.ScissorPop();
            }
        }

        public override void OnMouseDown(object sender, MouseEventArgs e)
        {
            base.OnMouseDown(sender, e);

            this.UpdateHits(this.lastMousePosition, e.Position);

            if (this.focusedWidget != null && !this.actHits.Contains(this.focusedWidget))
            {
                MouseEventArgs args = new MouseEventArgs(e.LeftButton, e.RightButton, e.Position, false);
                this.focusedWidget.OnMouseDown(this.focusedWidget, args);
            }

            if (!e.WidgetUnderMouse)
            {
                return;
            }

            foreach (Widget w in this.actHits)
            {
                if (!w.Visible)
                {
                    continue;
                }

                ////Trace.WriteLine("CW MouseDown: " + w.Name);
                w.OnMouseDown(w, e);

                if (w.CanFocus && e.LeftButton)
                {
                    w.SetFocus();
                }
            }
        }

        public override void OnMouseUp(object sender, MouseEventArgs e)
        {
            base.OnMouseUp(sender, e);

            this.UpdateHits(this.lastMousePosition, e.Position);

            if (this.focusedWidget != null && !this.actHits.Contains(this.focusedWidget))
            {
                MouseEventArgs args = new MouseEventArgs(e.LeftButton, e.RightButton, e.Position, false);
                this.focusedWidget.OnMouseUp(this.focusedWidget, args);
            }

            if (!e.WidgetUnderMouse)
            {
                return;
            }

            foreach (Widget w in this.actHits)
            {
                if (!w.Visible)
                {
                    continue;
                }

                ////Trace.WriteLine("CW MouseUp: " + w.Name);
                w.OnMouseUp(w, e);
            }
        }

        public override void OnMouseMove(object sender, MouseEventArgs e)
        {
            base.OnMouseMove(sender, e);

            this.UpdateHits(this.lastMousePosition, e.Position);

            if (this.focusedWidget != null && !this.actHits.Contains(this.focusedWidget))
            {
                MouseEventArgs args = new MouseEventArgs(e.LeftButton, e.RightButton, e.Position, false);
                this.focusedWidget.OnMouseMove(this.focusedWidget, args);
            }

            if (!e.WidgetUnderMouse)
            {
                return;
            }

            foreach (Widget w in this.actHits)
            {
                if (!w.Visible)
                {
                    continue;
                }

                if (!this.prevHits.Contains(w))
                {
                    ////Trace.WriteLine("CW MouseEnter: " + w.Name);
                    w.OnMouseEnter(w, e);
                    this.prevHits.Add(w);
                }
                else
                {
                    ////Trace.WriteLine("CW MouseMove: " + w.Name);
                    w.OnMouseMove(w, e);
                }
            }

            for (int i = this.prevHits.Count - 1; i >= 0; i--)
            {
                Widget w = this.prevHits[i];
                if (!this.actHits.Contains(w))
                {
                    ////Trace.WriteLine("CW MouseLeave: " + w.Name);
                    w.OnMouseLeave(w, EventArgs.Empty);
                    this.prevHits.Remove(w);
                }
            }

            this.lastMousePosition = e.Position;
        }

        public override void OnMouseLeave(object sender, EventArgs e)
        {
            base.OnMouseLeave(sender, e);

            foreach (Widget w in this.prevHits)
            {
                if (!w.Visible)
                {
                    continue;
                }

                w.OnMouseLeave(w, EventArgs.Empty);
            }

            this.prevHits.Clear();
        }

        public IEnumerable<Widget> HitWidgets(params Vector2[] position)
        {
            foreach (Widget w in this.Widgets)
            {
                if (!w.Visible)
                {
                    continue;
                }

                foreach (Vector2 v in position)
                {
                    if (w.HitTest(v))
                    {
                        yield return w;
                        break;
                    }
                }
            }
        }

        public void UnfocusChild()
        {
            if (focusedWidget == null && focusBackup == null)
            {
                return;
            }

            Widget w = this.focusedWidget;

            this.focusedWidget = null;
            this.focusBackup = null;

            if (w != null)
            {
                w.Focused = false;
            }
        }

        public void Update(GameTime gameTime)
        {
            // Update widgets like the TextBox
            if (this.focusedWidget != null && this.focusedWidget is IUpdateableWidget)
            {
                ((IUpdateableWidget)this.focusedWidget).Update(gameTime);
            }
        }

        private void Init()
        {
            this.Border = true;
            this.prevHits = new FastList<Widget>(10);
            this.actHits = new FastList<Widget>(10);
        }

        private void UpdateHits(Vector2 lastMouse, Vector2 actMouse)
        {
            this.actHits.Clear();

            // list all widgets that are actually under the mouse pointer
            foreach (Widget w in this.HitWidgets(actMouse))
            {
                if (!w.Visible)
                {
                    continue;
                }

                this.actHits.Add(w);
            }
        }

        public void FitSizeToContents()
        {
            RectangleF rect = new RectangleF();

            foreach (Widget w in Widgets)
            {
                rect = RectangleF.Union(rect, w.ClientRectangle);
            }

            this.Size = new Vector2(rect.Width, rect.Height);
        }
    }
}
